package com.baoyouqun.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import com.alibaba.cola.dto.Response;
import com.alibaba.cola.dto.SingleResponse;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.baoyouqun.base.utils.ConvertUtils;
import com.baoyouqun.base.utils.ExcelTool;
import com.baoyouqun.domain.CreateCmd.InvoiceRecordCreateCmd;
import com.baoyouqun.domain.Query.InvoiceRecordPageQuery;
import com.baoyouqun.domain.UpdateCmd.InvoiceRecordUpdateCmd;
import com.baoyouqun.domain.VO.InvoiceRecordVO;
import com.baoyouqun.domain.VO.OrderRecordVO;
import com.baoyouqun.domain.VO.UserVO;
import com.baoyouqun.entity.InvoiceRecord;
import com.baoyouqun.entity.OrderRecord;
import com.baoyouqun.entity.User;
import com.baoyouqun.mapper.InvoiceRecordMapper;
import com.baoyouqun.service.InvoiceRecordService;
import com.baoyouqun.service.OrderRecordService;
import com.baoyouqun.service.UserService;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.io.OutputStream;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

@Slf4j
@Service
public class InvoiceRecordServiceImpl extends ServiceImpl<InvoiceRecordMapper, InvoiceRecord> implements InvoiceRecordService {

    @Resource
    private InvoiceRecordMapper invoiceRecordMapper;

    @Resource
    private UserService userService; // 关联用户表：查申请发票人姓名、手机号

    @Resource
    private OrderRecordService orderRecordService; // 关联订单表：查订单号、订单类型

    // ---------------------- 原有基础方法（优化，补充VO扩展信息） ----------------------
    @Override
    public SingleResponse<InvoiceRecordVO> create(InvoiceRecordCreateCmd cmd) {
        if (Objects.isNull(cmd) || StrUtil.isBlank(cmd.getUserId()) || StrUtil.isBlank(cmd.getOrderId())) {
            return SingleResponse.buildFailure("100101", "用户ID、订单ID不能为空");
        }

        InvoiceRecord entity = ConvertUtils.copyBean(cmd, InvoiceRecord.class);
        entity.setStatus((byte) 1); // 新增默认“未开具”
        entity.setCreateTime(LocalDateTime.now());

        Boolean saveResult = save(entity);
        if (!saveResult) {
            return SingleResponse.buildFailure("100102", "创建发票记录失败");
        }

        try {
            OrderRecordVO orderRecord = orderRecordService.selectById(entity.getOrderId());
            if (orderRecord != null) {
                orderRecord.setInvoiceStatus("1");
                orderRecordService.updateById(ConvertUtils.copyBean(orderRecord, OrderRecord.class));
            }
        } catch (Exception e) {
        }

        InvoiceRecordVO vo = ConvertUtils.copyBean(entity, InvoiceRecordVO.class);
        fillVoExtInfo(vo); // 补充用户/订单/状态描述
        return SingleResponse.of(vo);
    }

    @Override
    public Response update(InvoiceRecordUpdateCmd cmd) {
        if (Objects.isNull(cmd) || StrUtil.isBlank(cmd.getId())) {
            return Response.buildFailure("100301", "参数或ID不能为空");
        }

        InvoiceRecord oldEntity = getById(cmd.getId());
        if (Objects.isNull(oldEntity)) {
            return Response.buildFailure("100302", "未查询到发票记录");
        }

        InvoiceRecord newEntity = ConvertUtils.copyBean(cmd, InvoiceRecord.class);
        newEntity.setCreateTime(oldEntity.getCreateTime()); // 禁止修改创建时间

        Boolean updateResult = updateById(newEntity);
        return updateResult ? Response.buildSuccess() : Response.buildFailure("100303", "更新发票记录失败");
    }

    @Override
    public InvoiceRecordVO selectById(String id) {
        if (StrUtil.isBlank(id)) {
            return null;
        }

        InvoiceRecord entity = invoiceRecordMapper.selectById(id);
        if (Objects.isNull(entity)) {
            return null;
        }

        InvoiceRecordVO vo = ConvertUtils.copyBean(entity, InvoiceRecordVO.class);
        fillVoExtInfo(vo); // 补充扩展信息
        return vo;
    }

    // ---------------------- 新增核心方法实现（适配页面） ----------------------

    /**
     * 条件筛选分页查询（关联用户/订单表补充信息）
     */
    @Override
    public IPage<InvoiceRecordVO> pageVo(InvoiceRecordPageQuery query) {
        // 1. 参数初始化（默认值处理）
        if (Objects.isNull(query)) {
            query = new InvoiceRecordPageQuery();
        }
        Long current = Objects.nonNull(query.getCurrent()) ? query.getCurrent() : 1L;
        Long size = Objects.nonNull(query.getSize()) ? query.getSize() : 10L;

        // 2. 构建筛选条件（申请人、手机号、开具状态）
        LambdaQueryWrapper<InvoiceRecord> wrapper = buildQueryWrapper(query);

        // 3. MP分页查询（查原始实体）
        Page<InvoiceRecord> entityPage = new Page<>(current, size);
        entityPage = this.page(entityPage, wrapper);

        // 4. 实体转VO + 批量补充扩展信息（减少数据库查询次数）
        List<InvoiceRecordVO> voList = ConvertUtils.copyBeanList(entityPage.getRecords(), InvoiceRecordVO.class);
        batchFillVoExtInfo(voList);

        // 5. 封装VO分页结果
        Page<InvoiceRecordVO> voPage = new Page<>(current, size, entityPage.getTotal());
        voPage.setRecords(voList);
        return voPage;
    }

    /**
     * Hutool导出Excel（按筛选条件全量导出）
     */
    @Override
    public void exportInvoiceRecords(InvoiceRecordPageQuery query, HttpServletResponse response) {
        try {
            // 1. 全量查询数据（不分页，查所有符合条件的记录）
            if (Objects.isNull(query)) {
                query = new InvoiceRecordPageQuery();
            }
            query.setCurrent(1L);
            query.setSize(Long.MAX_VALUE); // 最大条数，确保全量导出
            IPage<InvoiceRecordVO> allDataPage = pageVo(query);
            List<InvoiceRecordVO> dataList = allDataPage.getRecords();

            if (CollUtil.isEmpty(dataList)) {
                throw new RuntimeException("无符合条件的发票记录，无法导出");
            }

            // 2. 设置响应头（下载模式，中文文件名编码）
            setExportResponseHeader(response, "发票记录导出");

            // 3. Hutool ExcelWriter构建表头和数据
            try (OutputStream outputStream = response.getOutputStream();
                 ExcelWriter writer = ExcelUtil.getWriter(true)) { // true=XLSX格式（支持大量数据）
                // 3.1 定义表头（与页面表格列一一对应）
                writer.addHeaderAlias("userName", "申请发票人");
                writer.addHeaderAlias("orderId", "申请订单号");
                writer.addHeaderAlias("orderType", "订单类型");
                writer.addHeaderAlias("totalAmount", "发票金额（元）");
                writer.addHeaderAlias("invoiceTitleTypeDesc", "抬头类型");
                writer.addHeaderAlias("buyerName", "购方名称");
                writer.addHeaderAlias("buyerTaxNo", "购方税号");
                writer.addHeaderAlias("remark", "备注内容");
                writer.addHeaderAlias("invoiceStatusDesc", "开具状态");
                writer.addHeaderAlias("createTime", "申请时间");

                // 3.2 写入数据（只导出表头定义的字段）
                writer.write(dataList, true); // true=忽略空值

                // 3.3 自动列宽（优化展示）
                writer.autoSizeColumnAll();

                // 3.4 写出到响应流
                writer.flush(outputStream, true);
            }

        } catch (IOException e) {
            log.error("Hutool导出发票记录Excel IO异常", e);
            throw new RuntimeException("导出失败：IO错误，" + e.getMessage());
        } catch (Exception e) {
            log.error("Hutool导出发票记录异常", e);
            throw new RuntimeException("导出失败：" + e.getMessage());
        }
    }

    /**
     * 事务性更新开具状态（避免数据不一致）
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public Response updateInvoiceStatus(String id, Byte invoiceStatus) {
        // 1. 参数校验
        if (StrUtil.isBlank(id)) {
            return Response.buildFailure("100302", "发票记录ID不能为空");
        }
        if (!Objects.equals(invoiceStatus, (byte) 1) && !Objects.equals(invoiceStatus, (byte) 2)) {
            return Response.buildFailure("100304", "状态不合法（仅支持1-未开具/2-已开具）");
        }

        // 2. 校验记录存在性
        InvoiceRecord entity = getById(id);
        if (Objects.isNull(entity)) {
            return Response.buildFailure("100302", "未查询到发票记录");
        }

        // 3. 状态未变化，直接返回成功（避免无效更新）
        if (Objects.equals(entity.getStatus(), invoiceStatus)) {
            String statusDesc = invoiceStatus == 1 ? "未开具" : "已开具";
            return Response.buildSuccess();
        }

        // 4. 更新状态和时间
        entity.setStatus(invoiceStatus);
        Boolean updateResult = updateById(entity);

        if (updateResult) {
            OrderRecordVO orderRecord = orderRecordService.selectById(entity.getOrderId());
            if (orderRecord != null) {
                orderRecord.setInvoiceStatus(String.valueOf(invoiceStatus));
            }
        }

        // 5. 返回结果
        String statusDesc = invoiceStatus == 1 ? "未开具" : "已开具";
        return updateResult ? Response.buildSuccess() : Response.buildFailure("100303", "状态更新失败");
    }

    // ---------------------- 工具方法（复用逻辑，减少代码冗余） ----------------------

    /**
     * 构建筛选条件（申请人模糊匹配、手机号模糊匹配、开具状态精确匹配）
     */
    private LambdaQueryWrapper<InvoiceRecord> buildQueryWrapper(InvoiceRecordPageQuery query) {
        LambdaQueryWrapper<InvoiceRecord> wrapper = new LambdaQueryWrapper<>();

        // 关联用户表筛选：申请人姓名（需先查用户ID列表，再IN查询）
        if (StrUtil.isNotBlank(query.getRealName())) {
            List<String> userIdList = userService.listUserIdsByName(query.getRealName()); // 假设用户Service有此方法
            if (CollUtil.isNotEmpty(userIdList)) {
                wrapper.in(InvoiceRecord::getUserId, userIdList);
            } else {
                wrapper.in(InvoiceRecord::getUserId, ""); // 无匹配用户，返回空结果
            }
        }

        // 关联用户表筛选：手机号（同理，先查用户ID列表）
        if (StrUtil.isNotBlank(query.getPhoneNumber())) {
            List<String> userIdList = userService.listUserIdsByCellphone(query.getPhoneNumber()); // 假设用户Service有此方法
            if (CollUtil.isNotEmpty(userIdList)) {
                wrapper.in(InvoiceRecord::getUserId, userIdList);
            } else {
                wrapper.in(InvoiceRecord::getUserId, ""); // 无匹配用户，返回空结果
            }
        }

        // 开具状态精确匹配
        if (Objects.nonNull(query.getStatus())) {
            wrapper.eq(InvoiceRecord::getStatus, query.getStatus());
        }

        // 按申请时间倒序（最新的在前面）
        wrapper.orderByDesc(InvoiceRecord::getCreateTime);

        return wrapper;
    }

    /**
     * 批量补充VO扩展信息（用户姓名、订单号、订单类型、状态描述）
     * 优化点：批量查询用户/订单，避免循环查库（提升性能）
     */
    private void batchFillVoExtInfo(List<InvoiceRecordVO> voList) {
        if (CollUtil.isEmpty(voList)) {
            return;
        }

        // 1. 批量查询用户信息（一次查库）
        List<String> userIds = voList.stream()
                .map(InvoiceRecordVO::getUserId)
                .filter(StrUtil::isNotEmpty)
                .distinct()
                .collect(Collectors.toList());
        Map<String, UserVO> userIdToUserMap = new HashMap<>();
        if (CollUtil.isNotEmpty(userIds)) {
            List<User> userList = userService.listByIds(userIds); // 假设用户Service有批量查询方法
            List<UserVO> userVOList = ConvertUtils.copyBeanList(userList, UserVO.class);
            userIdToUserMap = userVOList.stream()
                    .collect(Collectors.toMap(UserVO::getId, user -> user, (k1, k2) -> k1));
        }

        // 2. 批量查询订单信息（一次查库）
        List<String> orderIds = voList.stream()
                .map(InvoiceRecordVO::getOrderId)
                .filter(StrUtil::isNotEmpty)
                .distinct()
                .collect(Collectors.toList());
        Map<String, OrderRecord> orderIdToOrderMap = new HashMap<>();
        if (CollUtil.isNotEmpty(orderIds)) {
            List<OrderRecord> orderList = orderRecordService.listByIds(orderIds); // 假设订单Service有批量查询方法
            orderIdToOrderMap = orderList.stream()
                    .collect(Collectors.toMap(OrderRecord::getId, order -> order, (k1, k2) -> k1));
        }

        // 3. 填充VO扩展信息
        for (InvoiceRecordVO vo : voList) {
            // 3.1 填充用户信息（申请发票人姓名）
            UserVO userVO = userIdToUserMap.get(vo.getUserId());
            if (Objects.nonNull(userVO)) {
                vo.setUserName(userVO.getName());
            } else {
                vo.setUserName("未知用户");
            }

            // 3.2 填充订单信息（订单号、订单类型）
            OrderRecord order = orderIdToOrderMap.get(vo.getOrderId());
            if (Objects.nonNull(order)) {
                vo.setOrderId(order.getId()); // 假设订单表有orderNo字段
                vo.setOrderType(order.getType()); // 假设订单表有orderType字段
            }

            // 3.3 填充状态中文描述（抬头类型、开具状态）
            vo.setInvoiceStatusDesc(getInvoiceStatusDesc(vo.getStatus()));

            // 3.4 格式化申请时间（避免前端显示乱码）
            if (Objects.nonNull(vo.getCreateTime())) {
                vo.setCreateTime(vo.getCreateTime()); // 若需自定义格式，可加DateTimeFormatter
            }
        }
    }

    /**
     * 单个VO补充扩展信息（调用批量方法，统一逻辑）
     */
    private void fillVoExtInfo(InvoiceRecordVO vo) {
        if (Objects.isNull(vo)) {
            return;
        }
        batchFillVoExtInfo(List.of(vo));
    }

    /**
     * 抬头类型转中文描述（1-企业单位，2-非企业单位，3-个人）
     */
    private String getTitleTypeDesc(Byte titleType) {
        return switch (Objects.requireNonNullElse(titleType, (byte) 0)) {
            case 1 -> "企业单位";
            case 2 -> "非企业单位";
            case 3 -> "个人";
            default -> "未知类型";
        };
    }

    /**
     * 开具状态转中文描述（1-未开具，2-已开具）
     */
    private String getInvoiceStatusDesc(Byte invoiceStatus) {
        return switch (Objects.requireNonNullElse(invoiceStatus, (byte) 0)) {
            case 1 -> "未开具";
            case 2 -> "已开具";
            default -> "未知状态";
        };
    }

    /**
     * 设置导出响应头（下载模式+中文文件名）
     */
    private void setExportResponseHeader(HttpServletResponse response, String fileNamePrefix) {
        // 1. 设置内容类型（Excel）
        response.setContentType("application/vnd.ms-excel;charset=utf-8");        // 2. 生成带时间戳的文件名（避免重复）
        String timeSuffix = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
        String fileName = fileNamePrefix + "_" + timeSuffix + ".xlsx";
        // 3. 处理中文文件名编码（避免乱码）
        try {
            ExcelTool.setExportFileName(response, "发票记录导出");

        } catch (Exception e) {
            log.error("导出文件名编码失败", e);
            throw new RuntimeException("文件名编码错误：" + e.getMessage());
        }
        // 4. 禁用缓存（确保下载最新数据）
        response.setHeader("Pragma", "no-cache");
        response.setHeader("Cache-Control", "no-store, no-cache");
        response.setDateHeader("Expires", 0);
    }

}